<?php

namespace shisou\tpgii\handler;

use think\facade\Db;
use think\migration\Migrator;

class DataBase
{
    private $tables;
    private $cnt = 0;

    public function __construct(array $tables)
    {
        $this->tables = $tables;
        $this->cnt    = $this->getCnt();
    }

    public function dataSql()
    {
        $sqlFilePath = "/database/data.sql";
        if (file_exists(root_path() . $sqlFilePath)) {

            $content = '';
            $content .= '<?php' . "\n";
            $content .= '' . "\n";
            $content .= 'use think\facade\Db;' . "\n";
            $content .= 'use shisou\tpgii\lib\MAData;' . "\n";
            $content .= 'use think\migration\Migrator;' . "\n";
            $content .= 'use think\migration\db\Column;' . "\n";
            $content .= '' . "\n";
            $content .= 'class  DataBase' . $this->cnt . ' extends Migrator' . "\n";
            $content .= '{' . "\n";
            $content .= '    /**
     * Run the migration.
     *
     * @return void
     */' . "\n";
            $content .= '    public function up()' . "\n";
            $content .= '    {' . "\n";
            $content .= '        $sql = file_get_contents(root_path() . "' . $sqlFilePath . '");  // 导入的SQL文件路径' . "\n";
            $content .= '        $this->execute($sql);' . "\n";
            $content .= '    }' . "\n";
            $content .= '' . "\n";
            $content .= '    /**
     * Reverse the migration.
     *
     * @return void
     */' . "\n";
            $content .= '    public function down()' . "\n";
            $content .= '    {' . "\n";
            $content .= '        // 可选的，如果需要撤销迁移操作' . "\n";
            $content .= '    }' . "\n";
            $content .= '}' . "\n";
            $file    = root_path() . '/database/migrations/' . date('YmdHis') . '_data_base_' . $this->cnt . '.php';
            file_put_contents($file, $content);
        } else {
            dd(root_path() . '/database 目录下data.sql文件不存在');
        }
    }

    public function run()
    {
        $content     = '';
        $content     .= '<?php' . "\n";
        $content     .= '' . "\n";
        $content     .= 'use think\facade\Db;' . "\n";
        $content     .= 'use think\migration\Migrator;' . "\n";
        $content     .= 'use think\migration\db\Column;' . "\n";
        $content     .= '' . "\n";
        $content     .= 'class  DataBase' . $this->cnt . ' extends Migrator' . "\n";
        $content     .= '{' . "\n";
        $content     .= '    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will
     * automatically reverse them when rolling back:
     *
     * createTable
     * renameTable
     * addColumn
     * renameColumn
     * addIndex
     * addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */' . "\n";
        $content     .= '    public function change()' . "\n";
        $content     .= '    {' . "\n";
        $initSqlPath = root_path() . 'database/init.php';
        if (file_exists($initSqlPath)) {
            $initSql = include_once root_path() . "database/init.php";
            $content .= '        $initSql = include_once root_path() . "database/init.php";' . "\n";
        }
        $content .= '' . "\n";

        // loop tables
        foreach ($this->tables as $tb) {
            $singleTableSql = '';
            $singleTableSql .= $this->createSql($tb, $initSqlPath, $initSql);
            $content        .= $singleTableSql;
        }

        $content .= '    }' . "\n";
        $content .= '}' . "\n";
        $file    = root_path() . '/database/migrations/' . date('YmdHis') . '_data_base_' . $this->cnt . '.php';
        file_put_contents($file, $content);
    }

    public function createSql($tb, $initSqlPath, $initSql = null)
    {
        $content = '';
        if (Db::query("SHOW TABLES LIKE '{$tb['tableName']}'")) {
            //            $content = '        if ($this->hasTable("' . $tb['tableName'] . '")) {' . "\n";
            $content .= '        $this->table("' . $tb['tableName'] . '")' . "\n";
            $content .= $this->updateColumn($tb);
            $content .= '            ->update();' . "\n";
            $content .= '' . "\n";
            //        $content .= '        } else {' . "\n";
        } else {
            $content .= '        $this->table("' . $tb['tableName'] . '", ["primary_key" => ["id"], "engine" => "InnoDb", "comment" => "' . $tb['tableNameCn'] . '"])' . "\n";
            $content .= '            ->addColumn("created_at", "datetime", ["null" => false, "default" => "CURRENT_TIMESTAMP", "comment" => "创建时间"])' . "\n";
            $content .= '            ->addColumn("updated_at", "datetime", ["null" => false, "default" => "0000-00-00 00:00:00", "comment" => "更新时间"])' . "\n";
            $content .= '            ->addColumn("status", "tinyinteger", ["null" => false, "limit" => "1", "default" => "1", "comment" => "状态"])' . "\n";

            foreach ($tb['cols'] as $cols) {
                $column  = $this->getColumn($cols);
                $content .= '            ->addColumn("' . $cols['col'] . '", "' . $column['type'] . '", ' . $column['options'] . ')' . "\n";
            }
            $content .= '            ->create();' . "\n";

            if (file_exists($initSqlPath) && $initSql[$tb['tableName']]) {
                $content .= '        $this->table("' . $tb['tableName'] . '")' . "\n";
                $content .= '            ->insert($initSql["' . $tb['tableName'] . '"])' . "\n";
                $content .= '            ->save();' . "\n";
            }
            //            $content .= '        }' . "\n";
            $content .= '' . "\n";
        }

        return $content;
    }

    public function updateColumn($tb)
    {
        // 获取sql表字段
        $tableName = [];
        foreach ($tb['cols'] as $col) {
            $tableName[] = $col['col'];
        }

        // 获取数据表的所有字段
        $existingColumns = array_values(array_slice(Db::name($tb['tableName'])
            ->getTableFields(), 4));

        // 找出新增字段
        $addedFields = array_diff($tableName, $existingColumns);
        // 找出缺少字段
        $removedFields = array_diff($existingColumns, $tableName);

        $content = '';
        // 先更新表字段类型和注释
        foreach ($tb['cols'] as $cols) {
            if (in_array($cols['col'], $existingColumns)) {
                $column  = $this->getColumn($cols);
                $content .= '            ->changeColumn("' . $cols['col'] . '", "' . $column['type'] . '", ' . $column['options'] . ')' . "\n";
            }
        }
        // 然后再新增
        if (count($addedFields) > 0) {
            $affter = $existingColumns[array_key_first($addedFields) - 1];
            krsort($addedFields);
            foreach ($addedFields as $k => $v) {
                if (!array_key_exists($k - 1, $addedFields)) {
                    $affter = $existingColumns[$k - 1];
                }

                // TODO 判断下是修改了还是新增
                $column            = $this->getColumn($tb['cols'][$k]);
                $column['options'] = substr($column['options'], 0, -1) . ', "after" => "' . $affter . '"]';
                $content           .= '            ->addColumn("' . $v . '", "' . $column['type'] . '", ' . $column['options'] . ')' . "\n";
            }
        }

        // 最后再删除
        if (count($removedFields) > 0) {
            foreach ($removedFields as $k => $v) {
                $content .= '            ->removeColumn("' . $v . '")' . "\n";
            }
        }

        return $content;
    }

    public function getColumn($cols)
    {
        switch ($cols['type']) {
            case 'INT':
                $type    = 'integer';
                $options = '["limit" => ' . $cols['len'] . ', "default" => 0, "comment" => "' . $cols['colCn'] . '"]';
                break;
            case 'TINYINT':
                $type    = 'tinyinteger';
                $options = '["limit" => ' . $cols['len'] . ', "default" => 0, "comment" => "' . $cols['colCn'] . '"]';
                break;
            case 'DATETIME':
                $type    = 'datetime';
                $options = '["comment" => "' . $cols['colCn'] . '"]';
                break;
            case 'TIMESTAMP':
                $type    = 'timestamp';
                $options = '["comment" => "' . $cols['colCn'] . '"]';
                break;
            case 'DATE':
                $type    = 'date';
                $options = '["comment" => "' . $cols['colCn'] . '"]';
                break;
            case 'TIME':
                $type    = 'time';
                $options = '["default" => "", "comment" => "' . $cols['colCn'] . '"]';
                break;
            case 'FLOAT':
                $type    = 'float';
                $len     = explode(',', $cols['len']);
                $options = '["precision" => ' . $len[0] . ', "scale" => ' . ($len[1] ?? 2) . ', "default" => 0, "comment" => "' . $cols['colCn'] . '"]';
                break;
            case 'DECIMAL':
                $type    = 'decimal';
                $len     = explode(',', $cols['len']);
                $options = '["precision" => ' . $len[0] . ', "scale" => ' . ($len[1] ?? 2) . ', "default" => 0, "comment" => "' . $cols['colCn'] . '"]';
                break;
            case 'TEXT':
                $type    = 'text';
                $options = '["comment" => "' . $cols['colCn'] . '"]';
                break;
            case 'LONGTEXT':
                $type    = 'text';
                $options = '["comment" => "' . $cols['colCn'] . '"]';
                break;
            case 'JSON':
                $type    = 'text';
                $options = '["comment" => "' . $cols['colCn'] . '"]';
                break;
            default:
                $type    = 'string';
                $options = '["limit" => ' . ($cols['len'] ?? 255) . ', "default" => "", "comment" => "' . $cols['colCn'] . '"]';
                break;
        }

        return [
            'type'    => $type,
            'options' => $options,
        ];
    }

    public function getCnt()
    {
        $cnt = Db::table('migrations')
            ->order('version', 'desc')
            ->count();

        $directory    = root_path() . '/database/migrations/';
        $targetString = "_data_base_" . $cnt . '.php';
        // 获取目录中的所有文件和目录
        $files = scandir($directory);
        // 遍历文件
        foreach ($files as $file) {
            // 排除 "." 和 ".." 目录
            if ($file != "." && $file != "..") {
                // 检查文件名是否包含目标字符串
                if (strpos($file, $targetString) !== false) {
                    // 删除文件
                    unlink($directory . "/" . $file);
                }
            }
        }

        return $cnt;
    }
}
